using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace LogoFX.Infra.MarkupExtensions
{
	internal class Persistency
	{
		#region Fields

		private readonly Dictionary<string, Element> _elementLookupTable = new Dictionary<string, Element>();
		private State _state; 

		#endregion

		#region Initializers

		internal Persistency()
		{
			this._state = new State();
		}
 
		#endregion

		#region Operations

        internal void Load(Stream stream,List<Type> known)
        {
            try
            {
                if (stream.Length > 0)
                {
                    XmlObjectSerializer serializer = new NetDataContractSerializer();
                    //XmlSerializer serializer = new XmlSerializer(typeof (State));
                    //_state = (State) serializer.Deserialize(stream);
                    _state = (State)serializer.ReadObject(stream);
                }
            }
            catch
            {
                // Failed to deserialize the stream. Using an empty state
            }
            InitializeLookupTables();
        }

        internal void Save(Stream stream, List<Type> known)
		{
            XmlObjectSerializer serializer = new NetDataContractSerializer();
            
            MemoryStream m = new MemoryStream();
            XmlTextWriter tw = new XmlTextWriter(m, Encoding.UTF8)
           {
               Formatting = Formatting.Indented,
               Indentation = 4,
               IndentChar = " ".ToCharArray()[0]
           };
            serializer.WriteObject(tw, _state);
            tw.Flush();
            m.Position = 0;
            m.WriteTo(stream);
		}

        internal void Store(string uid,object o)
        {
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(o))
            {
                Update(uid,property.Name,property.GetValue(o)/*!=null?property.GetValue(o).ToString():string.Empty*/);
            }
        }

        internal void Load(string uid, object o)
        {
            foreach (Property property in _elementLookupTable[uid].Properties)
            {
                PropertyDescriptor pd = TypeDescriptor.GetProperties(o).Find(property.Name, false);
                if(pd!=null)
                    pd.SetValue(o,GetValue(uid,property.Name));                
            }
        }

        internal void Update(string uid, string propertyName, object value)
        {
            if (!_elementLookupTable.ContainsKey(uid))
            {
                // Add new element
                Element element = new Element();
                element.UId = uid;
                Property property = new Property();
                property.Name = propertyName;
                property.Value = value;
                element.Properties.Add(property);
                // Update the state
                _state.Elements.Add(element);
                // Update the lookup table
                _elementLookupTable.Add(element.UId, element);
                return;
            }

            Element foundElement = _elementLookupTable[uid];
            if (!Contains(foundElement, propertyName))
            {
                // Add new property
                Property property = new Property();
                property.Name = propertyName;
                property.Value = value;
                foundElement.Properties.Add(property);
                return;
            }

            // Update existing property
            Property foundProperty = GetProperty(foundElement, propertyName);
            foundProperty.Value = value;
        }

        internal bool Contains(string uid, string propertyName)
        {
            return _elementLookupTable.ContainsKey(uid) &&
                Contains(_elementLookupTable[uid], propertyName);
        }

        internal object GetValue(string uid, string propertyName)
        {
            Element element = _elementLookupTable[uid];
            return GetValue(element, propertyName);
        }

        #endregion

        #region Internal Operations

        private void InitializeLookupTables()
        {
            foreach (Element element in _state.Elements)
            {
                _elementLookupTable.Add(element.UId, element);
            }
        }

        private bool Contains(Element element, string propertyName)
        {
            foreach (Property property in element.Properties)
            {
                if (property.Name == propertyName)
                {
                    return true;
                }
            }
            return false;
        }

        private object GetValue(Element element, string propertyName)
        {
            foreach (Property property in element.Properties)
            {
                if (property.Name == propertyName)
                {
                    return property.Value;
                }
            }
            return string.Empty;
        }

        private Property GetProperty(Element element, string propertyName)
        {
            foreach (Property property in element.Properties)
            {
                if (property.Name == propertyName)
                {
                    return property;
                }
            }
            return null;
        }



		#endregion
	}
}
